package com.steakliu.sun.connections;

import com.steakliu.sun.disuse.api.ConnectionsInfo;
import com.steakliu.sun.disuse.api.DisuseStrategy;
import com.steakliu.sun.spi.loader.ExtensionLoader;
import io.netty.channel.Channel;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 功能说明： 连接管理器
 * <p>
 * Original @Author: steakliu-刘牌, 2023-02-05  20:10
 * <p>
 * Copyright (C)2020-2022  steakliu All rights reserved.
 */
public class ConnectionsManager {

    private volatile Map<String, ConnectionsInfo> CONNECTIONS_MAP = new ConcurrentHashMap<>();

    private final DisuseStrategy disuseStrategy;

    private static volatile ConnectionsManager connectionsManager;

    private final int maxConnections;

    public ConnectionsManager(int maxConnections, String connectionsDisuseStrategyType){
        this.maxConnections = maxConnections <=0 ? Integer.MAX_VALUE : maxConnections;
        this.disuseStrategy = ExtensionLoader.getExtension(DisuseStrategy.class, connectionsDisuseStrategyType);
    }


    public static ConnectionsManager getInstance(int maxConnections, String connectionsDisuseStrategyType){
        if (null == connectionsManager){
            synchronized (ConnectionsManager.class){
                if (null == connectionsManager){
                    connectionsManager = new ConnectionsManager(maxConnections, connectionsDisuseStrategyType);
                }
            }
        }
        return connectionsManager;
    }

    public void  add(Channel channel){
        ConnectionsInfo connectionsInfo = new ConnectionsInfo(channel);
        if (checkConnections(channel)){
            CONNECTIONS_MAP.put(getChannelId(channel), connectionsInfo);
        }
    }

    public void remove(Channel channel){
        CONNECTIONS_MAP.remove(getChannelId(channel));
    }

    public void update(Channel channel){
        ConnectionsInfo connectionsInfo = CONNECTIONS_MAP.get(getChannelId(channel));
        connectionsInfo.setLastUseTime(System.currentTimeMillis());
        connectionsInfo.incrementAndGet();
        CONNECTIONS_MAP.put(getChannelId(channel), connectionsInfo);
    }

    private boolean checkConnections(Channel channel){
        ArrayList<ConnectionsInfo> connectionsInfoList = new ArrayList<>(CONNECTIONS_MAP.values());
        if (connectionsInfoList.size() > maxConnections){
            try {
                ConnectionsInfo connectionsInfo = disuseStrategy.selectConnection(connectionsInfoList);
                if (null != connectionsInfo){
                    connectionsInfo.getChannel().close();
                    CONNECTIONS_MAP.remove(getChannelId(channel));
                }
            }catch (Exception e){
                e.printStackTrace();
                return false;
            }
        }
        return true;
    }

    private String getChannelId(Channel channel){
        return channel.id().asLongText();
    }
}
